import { typeIs } from "./verify";

/** 获取对象的属性
 * @param obj 对象
 * @param property 属性
 * @example
 *  PickProperty({ a: 1, b: 2 }, 'a')     // 1
 *  PickProperty({ a: 1, b: 2 }, 'c')     // undefined
 */
export function pickProperty<T, K extends keyof T>(obj: T, property: K) {
  return obj[property] || undefined;
}

/** 创建长度为len的数组
 * @param length 长度
 * @param createItemFunc 创建元素方法
 * @example
 * createRepeatItem(3, (idx) => idx)     // [0, 1, 2]
 * createRepeatItem(3, (idx) => idx + 1) // [1, 2, 3]
 * createRepeatItem(2, false)            // [false, false]
 */
export function createRepeatItem<T>(length: number, createItemFunc: T | ((idx: number) => T)): T[] {
  return Array.from({ length }).map((_, idx) => typeIs(createItemFunc, 'Function') ? (createItemFunc as Function)(idx) : createItemFunc)
}

type Array2treeRESULT<T> = (T & {[k: string]: T[]})
/** 扁平化数组转树
 * @param items 元数据组
 * @param idKey id字段
 * @param parentIdKey pid字段
 * @param childrenKey children字段
 * @example
 * const data = [{id:1,pid:0}, {id:2,pid:0}, {id:2,pid:1}];
 * const res = array2tree(data, 'id', 'pid', 'children')
 * // [
 *  {id:1, pid:0, children:[
 *    {id:2, pid:1, children:[]}
 *  ]},
 *  {id:2, pid:0, children:[]}
 * ]
 */
export const array2tree = <T extends Record<string, any>, KEY extends keyof T, RESULT = Array2treeRESULT<T>>(
  items: T[],
  idKey: KEY = ('id' as KEY),
  parentIdKey: KEY = ('pid' as KEY),
  childrenKey: string = 'children'
) => {
  /** 导出结果 */
  const res: RESULT[] = []
  /** map暂存数据 */
  const map: Record<number, RESULT> = {}
  // 判断对象是否有某个属性
  let getHasOwnProperty = (obj: any, property: any) => Object.prototype.hasOwnProperty.call(obj, property)

  // 边做map存储，边找对应关系
  for (const i of items) {
    // @ts-ignore
    map[i[idKey]] = {...i, [childrenKey]: getHasOwnProperty(map, i[idKey]) ? map[i[idKey]][childrenKey] : []}

    const newItem = map[i[idKey]]

    if (i[parentIdKey] === 0) {
      res.push(newItem)
    } else {
      if (!getHasOwnProperty(map, i[parentIdKey])) {
        map[i[parentIdKey]] = { [childrenKey]: [] } as unknown as RESULT
      }

      // @ts-ignore
      map[i[parentIdKey]][childrenKey]?.push(newItem)
    }
  }
  return res
}

/** 组合算法，从m中选取n个数进行组合
 * @param data
 * @param size
 * @example const res = choose([1, 2, 3], 2) // [[1, 2], [1, 3], [2, 3]]
 */
export const choose = <T>(data: T[], size: number) => {
  var allResult: T[][] = [];

  const runner = (arr: T[], size: number, result: T[]) => {
    var arrLen = arr.length;
    if (size > arrLen) {
      return;
    }
    if (size == arrLen) {
      allResult.push(([] as T[]).concat(result, arr))
    } else {
      for (let i = 0; i < arrLen; i++) {
        var newResult = ([] as any[]).concat(result);
        newResult.push(arr[i]);

        if (size == 1) {
          allResult.push(newResult);
        } else {
          var newArr = ([] as any[]).concat(arr);
          newArr.splice(0, i + 1);
          runner(newArr, size - 1, newResult);
        }
      }
    }
  }

  runner(data, size, []);

  return allResult;
}
/** 字符串脱敏
 * @param str 元字符串
 * @param replaceStr 脱敏字符串
 * @param lenHead 头部不脱敏字数
 * @param lenTail 尾部不脱敏字数
 * @example desensitize('1234567890', '****', 3, 3) // 123****890
 */
export const desensitize = (str: string, replaceStr: string = '****', lenHead: number = 3, lenTail: number = 4): string => {
  const t = lenHead + lenTail
  const l = str.length
  return t > l
    ? `${str.slice(0, Math.ceil((l - 4) / 2))}${replaceStr}${str.slice(l - Math.floor((l - 4) / 2))}`
    : `${str.slice(0, lenHead)}${replaceStr}${str.slice(0, lenTail)}`
}
/** 手机号脱敏 */
export const desensitizeMobile = (str: string) => desensitize(str, '****', 3, 4);
/** web3地址脱敏
 * @example desensitizeWeb3Adds('0x1578000000000000000000000000000000006482') // 0x15...6482
 * @example desensitizeWeb3Adds('0x1578000000000000000000000000000000006482', 6) // 0x1578...6482
 */
export const desensitizeWeb3Adds = (str: string, start = 4) => desensitize(str, '....', start, 4);
/** 字符串截取
 * @param str 源字符串
 * @param option 截取规则
 * @example
 * capture('<span>{{ data.name }}</span>', ['{{', '}}']) // ['data.name']
 */
export const capture = (str: string, option: [string, string] = ['{{', '}}']): (null | string[]) => {
  const pattern = new RegExp(`[^${option[0]}\}]+(?=${option[1]})`, 'g')

  return str.match(pattern)
}
/** 数组去重
 * @param d 集合
 * @returns 去重结果
 * @example dupRemove([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]) // [1, 2, 3, 4, 5]
 */
export const dupRemove = <T>(d: T[]) : T[] => Array.from(new Set(d));
/** 数据分页
 * @param arr 待分页数组
 * @param len 分页长度
 */
export const paging = <T>(arr: T[], len: number): T[][] => {
  let idx = 0

  const res: T[][] = []

  while (idx < arr.length) {
     res.push(arr.slice(idx, idx += len))
  }

  return res
}
/** 集合排序
 * @param arr 集合
 * @param key 排序字段
 * @returns 排序后集合
 * @example sortByProp([{a: 3}, {a: 2}, {a: 4}], 'a') // [{a: 2}, {a: 3}, {a: 4}]
 */
export const sortByProp = <T extends any[], K extends keyof T>(arr: T, key: K): T => arr.sort((a, b) => a[key] - b[key]);
/** 数据净化，去除不必要的字段
 * @param entry 元数据
 * @param fields 保留字段
 * @example purify({a: 1, b: 2, c: 3}, ['a', 'b']) // {a: 1, b: 2}
 */
export const purify = <T, k extends keyof T>(entry: T, fields: k[]) => Object.fromEntries(fields.map(key => [key, entry[key]]));
/** 克隆数据/深拷贝
 * @param entry 元数据
 * @example clone({a: 1, b: 2, c: 3}) // {a: 1, b: 2, c: 3}
 *
 * 注：深拷贝会丢失函数，包含函数的对象不可以使用本函数
 */
export const clone = <T>(entry: T) => JSON.parse(JSON.stringify(entry));
/** 提取中文字符串
 * @param str
 * @example extractChinese('你好，世界') // '你好世界'
 */
export const getCnFromString = (str: string) => str.match(/[\u4e00-\u9fa5]/g)?.join('');
/** 数据分组
 * @example groupArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3) // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
 */
export const groupArray = <T>(arr: T[], len: number): T[][] => {
  const result = [];
  for (let i = 0; i < arr.length; i += len) {
    result.push(arr.slice(i, i + len));
  }
  return result;
}